import React, { useEffect, useRef, useState } from 'react';
import { Button, Menu, Card, Collapse, Select, message } from 'antd';
import { HomeFilled, EditOutlined, SaveOutlined } from '@ant-design/icons';
import { Graph, Shape } from '@antv/x6';
import { Snapline } from '@antv/x6-plugin-snapline';
import { Clipboard } from '@antv/x6-plugin-clipboard';
import { Selection } from '@antv/x6-plugin-selection';
import { Keyboard } from '@antv/x6-plugin-keyboard';
import { History } from '@antv/x6-plugin-history';
import AttrListItem from './AttrListItem';
import { Dnd } from '@antv/x6-plugin-dnd';
import style from './CanvasContainer.css';
import { calcVertices, frozenStationMeters, ports, modelPaths, showPorts, edgeAttrs } from './registerNode';

const { Panel } = Collapse;
const { Option } = Select;

let graph = null, dnd = null, timer = null, sourceDataMaps = {};
let metersMockData = [];
let cacheJSON = ''
for ( let i=0;i < 10; i++){
    metersMockData.push({ meterName:'电表00' + ( i + 1 ), key:i+1 });
}
let infoData = [
    { title:'冷量', value:213.4, unit:'kw' },
    { title:'回水温度', value:36.3, unit:'℃'},
    { title:'出水温度', value:26.3, unit:'℃'},
    { title:'压力', value:21.3, unit:'bar'},
    { title:'瞬时流量', value:4282, unit:'m³/h'}
];
let selectedIds = [];
function CanvasContainer({ onDispatch }){
    let [isEditing, setEditing] = useState(false);
    let [meterList, setMeterList] = useState(metersMockData);
    let [meterId, setMeterId] = useState('');
    let [currentCell, setCurrentCell] = useState({});
    let [attrs, setAttrs] = useState({});
    let [info, setInfo] = useState({});
    let [hide, setHide] = useState(false);
    const containerRef = useRef();
    const currentCellRef = useRef(currentCell);
    const isEditingRef = useRef(isEditing);
    
    useEffect(()=>{
        isEditingRef.current = isEditing;
        // 编辑模式下开启删除工具，文本编辑工具
        if ( graph ) {
            let nodes = graph.getNodes();
            if ( isEditing ) {
                nodes.forEach(node=>{
                    node.addTools([
                        {
                            name:'node-editor',
                            args:{
                                attrs:{ color:'#fff', backgroundColor:'rgba(0, 0, 0, 0.45)' }
                            }
                        },
                        {
                            name:'button-remove',
                            args:{
                                x:'100%',
                                y:0
                            }
                        }
                    ])
                })
            } else {
                nodes.forEach(node=>{
                    node.removeTools();
                });
            }
        }     
    },[isEditing])
    useEffect(()=>{
        // 初始化一个画布实例
        graph = new Graph({
            container:containerRef.current,
            width:containerRef.current.offsetWidth,
            height:containerRef.current.offsetHeight,
            mousewheel:{
                enabled:true,
                modifiers:['ctrl', 'meta']
            },
            // interacting:{
            //     edgeMovable:false,
            // },
            interacting:(cellView)=>{
                if ( isEditingRef.current ) {
                    return { edgeMovable:false };
                } else {
                    return { edgeMovable:false, nodeMovable:false }
                }
            },    
            // 配置节点连接桩的交互属性
            connecting:{
                router:'orth',
                connector:{
                    name:'rounded',
                    args:{
                        radius:4
                    }
                },
                connectionPoint:'anchor',
                allowBlank:false,
                allowLoop:false,
                snap:{ radius:20 },
                createEdge:({ sourceCell, sourceMagnet, sourceView })=>{
                    // 获取连接桩在画布中的坐标值,根据连接桩的位置动态生成路径上的锚点修正路径
                    return new Shape.Edge({
                        attrs:{
                            line:{
                                stroke: '#A2B1C3',
                                strokeWidth: 2,
                                strokeDasharray:'12,0',
                                targetMarker: {
                                    name: 'block',
                                    width: 12,
                                    height: 8,
                                },
                            }
                        },                       
                        zIndex:0
                    })
                },
                validateConnection({ targetMagnet }) {
                    return !!targetMagnet;
                },
            },    
            // 连线过程中，自动吸附到连接桩时高亮
            highlighting:{
                magnetAdsorbed:{
                    name:'stroke',
                    args:{
                        attrs: {
                            fill: '#5F95FF',
                            stroke: '#5F95FF'
                        },
                    }
                }
            },
            autoResize:true
        });
        graph.use(
            new Snapline({
                enabled:true
            })
        );
        graph.use(
            new Clipboard({
                enabled:true
            })
        );
        graph.use(
            new Selection({
                enabled:true,
                multiple:true,
                rubberband:true,
                movable:true,
                showNodeSelectionBox:true
            })
        );
        graph.use(
            new Keyboard({
                enabled:true,
                global:true
            })
        );
        graph.use(
            new History({
                enabled:true,
                stackSize:5
            })
        );
        graph.bindKey('ctrl+c',()=>{
            const cells = graph.getSelectedCells();
            if ( cells.length ) {
                graph.copy(cells);
            } else {
                return false;
            }
        })
        graph.bindKey('ctrl+v',()=>{
            if ( !graph.isClipboardEmpty() ){
                const cells = graph.paste({ offset:32 });
                graph.cleanSelection();
                graph.select(cells);
            }
        });
        graph.bindKey('ctrl+z',()=>{
            graph.undo();
        })
        dnd = new Dnd({
            target:graph,
        });
        // 监听边线移动事件，移动后更新关联的所有连接线
        graph.on('node:moved', ({ e, x, y, node })=>{
            // 获取跟移动节点相关联的所有Edges
            let allEdges = graph.getEdges();
            allEdges.forEach(edge=>{
                if ( edge.getSourceCell().id === node.id || edge.getTargetCell().id === node.id ){
                    let vertices = calcVertices(graph, edge);
                    if ( vertices.length ){
                        edge.setRouter('orth');
                    } else {
                        edge.setRouter('normal');
                    }
                    edge.setVertices(vertices);
                }     
            }) 
        })
        // 监听节点选中事件
        graph.on('cell:click',({ e, x, y, cell, view  })=>{
            // 将当前选中节点已绑定的设备记录起来
            if ( isEditingRef.current && currentCellRef.current.id !== cell.id ){
                // 当选中不同节点，将上一个节点已绑定的设备存储起来
                if ( currentCellRef.current.id ) {
                    let prevCell = graph.getCellById(currentCellRef.current.id);
                    if ( prevCell ) {
                        let temp = prevCell.getData();
                        let prevMeterId = temp && temp.meterId;
                        if ( prevMeterId ) {
                            selectedIds.push(prevMeterId);
                        }
                    }          
                }                
                setCurrentCell({ type:cell.shape, id:cell.id, title:cell.label });
            }
        })
        graph.on('blank:click',()=>{
            if ( isEditingRef.current && currentCellRef.current.id ){
                let prevCell = graph.getCellById(currentCellRef.current.id);
                if ( prevCell ) {
                    let temp = prevCell.getData();
                    if ( temp && temp.meterId ) {
                        selectedIds.push(temp.meterId);
                    }
                }      
                setCurrentCell({});
            }
        })
        graph.on('edge:connected', ({ isNew, edge })=>{
            // 添加连接线之后，根据连接桩的位置计算出vertices
            let vertices = calcVertices(graph, edge);
            edge.setVertices(vertices);
            if ( !vertices.length ){
                edge.setRouter('normal');
            }
        })   
        // 监听节点删除事件
        graph.on('node:mouseenter', ({ e, node, view })=>{
            if ( isEditingRef.current && node.shape === 'image' ) {
                // 显示所有节点的连接桩
                let ports = containerRef.current.querySelectorAll('.x6-port-body');
                showPorts(ports, true);
            }
            // 如果有关联设备，则打开模态弹窗
            let userData = node.getData();
            if ( node.shape === 'image' && userData && userData.meterId ) {        
                let size = node.size();
                // 获取相对于viewport坐标系的坐标系，转换成画布坐标系
                let { x, y } = node.position();
                let  viewportDom = containerRef.current.querySelectorAll('.x6-graph-svg-viewport')[0];
                if ( viewportDom ) {
                    let matrix = viewportDom.getCTM();
                    let absoluteX = x * matrix.a + matrix.e + size.width * matrix.a + 20;
                    let absoluteY = y * matrix.a  + matrix.f + ( size.height * matrix.a ) / 2 ;
                    setInfo({ x:absoluteX, y:absoluteY });
                }
            }
        })
        graph.on('node:mouseleave', ({ node })=>{
            if ( isEditingRef.current ) {
                let ports = containerRef.current.querySelectorAll('.x6-port-body');
                showPorts(ports, false);
            }   
            setInfo({});
        });
        graph.on('node:removed', ({ e, node })=>{
            let userData = node.getData();
            if ( userData && userData.meterId ) {
                selectedIds = selectedIds.filter(i=>i !== userData.meterId );
            }
        })  
        
        graph.centerContent();
        return ()=>{
            graph = null;
            timer = null;
            selectedIds = [];
            sourceDataMaps = {};
        }
    },[]);
    useEffect(()=>{
        currentCellRef.current = currentCell;
        if ( currentCell.id ) {
            let target = graph.getCellById(currentCell.id);
            if ( target ){
                let obj = target.getData();
                let bindMeterId = obj && obj.meterId;
                setMeterId(bindMeterId);
                // 切换节点时，将
                if ( currentCell.type === 'edge' ) {
                    // 获取边线的属性值对象
                    setAttrs(target.getAttrs().line);
                }
            }        
        } else {
            setMeterId('');
        }
    },[currentCell]);
    // 监听绑定的设备
    useEffect(()=>{
        if ( meterId ){
            let target = graph.getCellById(currentCell.id);
            let nodeData = target.getData();
            // 将电表绑定到当前节点上
            target.setData({ meterId });
            // 过滤掉之前关联的meterId
            selectedIds = selectedIds.filter(i=> i !== meterId );
            // 请求当前电表的数据
            fetchData()
            .then((bool)=>{
                target.setAttrs({
                    image:{
                        'xlink:href':bool ? modelPaths[nodeData.type].normalPath : modelPaths[nodeData.type].warningPath
                    }
                })
            })
        } 
    },[meterId])
    // 定期请求数据接口，更新所有终端节点状态
    function fetchData(){
        return new Promise((resolve, reject)=>{
            setTimeout(()=>{
                resolve(Boolean(Math.round(Math.random())));
            },1000)
        })
    }
    function updateAllNodes(){
        if ( graph ){
            let fetchNodes = graph.getNodes().filter(node=>node.shape === 'anchor-node' && node.getData().meterId);
            Promise.all(fetchNodes.map(()=>fetchData()))
            .then((dataList)=>{
                dataList.forEach((item, index)=>{
                    sourceDataMaps[fetchNodes[index].getData().meterId] = item;
                })
                fetchNodes.forEach((node, index)=>{
                    node.setAttrs({
                        body:{
                            fill:
                                dataList[index] 
                                ?                            
                                {
                                    type:'linearGradient',
                                    stops:[{ offset:'0%', color:'#97f497'}, { offset:'100%', color:'#61dc61'}]
                                } 
                                :
                                {
                                    type:'linearGradient',
                                    stops:[{ offset:'0%', color:'#ff5656'}, { offset:'100%', color:'#ff2222'}]
                                }
                        }                   
                    })
                })             
            })
        }
    }
    const startDrag = (e)=>{
        let target = e.currentTarget;
        let title = target.getAttribute('data-title');
        let type = target.getAttribute('data-type');
        let width = target.getAttribute('data-width');
        let height = target.getAttribute('data-height');
        // 判断拖拽的是哪个设备节点
        if ( graph ) {
            let node = graph.createNode({
                shape:'image',
                width:Number(width),
                height:Number(height),
                label:title,
                attrs:{
                    image:{
                        'xlink:href': modelPaths[type].path
                    },
                    label:{
                        refX:0.5,
                        refY:'100%',
                        refY2:20,
                        textAnchor:'middle'
                    }
                },             
                data:{ type },
                ports:{ ...ports },
                tools:[
                    {
                        name:'node-editor',
                        args:{
                            attrs:{ color:'#fff', backgroundColor:'rgba(0, 0, 0, 0.45)' }
                        }
                    },
                    {
                        name:'button-remove',
                        args:{
                            x:'100%',
                            y:0
                        }
                    },
                    {
                        name:'vertices',
                        args:{
                            attrs:{
                                fill:'#ccc'
                            }
                        }
                    }
                ]
            });
            dnd.start(node, e.nativeEvent);
        }
    }
    // 更新属性值
    const updateAttr = (attrName, parentAttrName, value, valueType )=>{
        let target = graph.getCellById(currentCell.id);
        if ( parentAttrName ) {            
            // 兼容属性值的不同类型 strokeDasharray:'12,8' 或者 targetMarker:{ type:'block', width:10, height:10 }
            target.setAttrs({
                line:{
                    [parentAttrName]: valueType === 'string' ? value :  { [attrName]:value }
                }           
            })
            setAttrs({ ...attrs, [parentAttrName]:valueType === 'string' ? value : { ...attrs[parentAttrName], [attrName]:value }});   
        } else {
            target.setAttrs({ line:{ [attrName]:value }});
            setAttrs({ ...attrs, [attrName]:value });
        }
    }
    const unbindMeter = ()=>{
        let target = graph.getCellById(currentCell.id);
        let { type, meterId } = target.getData();
        // 更新节点的状态为初始状态, 并解绑关联的电表
        target.setData({ meterId:'' });
        target.setAttrs({ image:{ 'xlink:href':modelPaths[type].path }});
        selectedIds = selectedIds.filter(i=>i !== meterId);
        setMeterId('');
    }
    return (
        <div style={{ width:'100%', height:'100%', position:'relative' }}>
            {/* 模态数据窗口 */}
            <div className={style['info-container']} style={{ 
                position:'absolute', 
                display:Object.keys(info).length ? 'block' : 'none',
                left:info.x + 'px',
                top:info.y + 'px'
            }}>
                <div className={style['info-title']}>制冷站冷量计</div>
                <div className={style['info-content']}>
                    {
                        infoData.map((item, index)=>(
                            <div key={index} className={style['info-item']}>
                                <div>{ item.title }</div>
                                <div className={style['symbol']}></div>
                                <div style={{ fontSize:'1.2rem', color:'#000' }}>{ item.value + item.unit }</div>
                            </div>
                        ))
                    }
                </div>
            </div>
            {
                isEditing 
                ?
                <div style={{ position:'absolute', right:'1rem', top:'0', zIndex:'10' }}>
                    <Button onClick={()=>setHide(!hide)} style={{ marginRight:'1rem' }}>{ hide ? '展开侧边' : '折叠侧边' }</Button>
                    <Button type='primary' icon={<SaveOutlined />} style={{ marginRight:'1rem' }} onClick={()=>{
                        if ( graph ) {
                            console.log(graph.copy);
                            // 测试复制节点的性能
                            let cells = graph.toJSON().cells;
                            let backupCells = [];
                            // 导出前删除一些属性，压缩json大小
                            cells.forEach(cell=>{
                                let obj = { ...cell };
                                if ( obj.shape === 'image' ) {
                                    obj.ports = { items:obj.ports.items.map(i=>i.id )}
                                    delete obj.tools
                                }
                                backupCells.push(obj);
                            });
                            let  viewportDom = containerRef.current.querySelectorAll('.x6-graph-svg-viewport')[0];
                            localStorage.setItem('json', JSON.stringify({ cells:backupCells, matrix:viewportDom.getAttribute('transform') }));                        
                            message.success('画布保存成功');
                        }
                    }}>保存</Button>
                    <Button onClick={()=>setEditing(false)}>退出编辑</Button>
                </div>
                :
                <div style={{ position:'absolute', right:'1rem', top:'0', zIndex:'10' }}>
                    <Button type='primary' style={{ marginRight:'1rem' }} onClick={()=>{
                        let json = localStorage.getItem('json');
                        let sourceData = json ? JSON.parse(json) : '';
                        if ( sourceData ) {
                            // 初始化所有节点的连接桩
                            sourceData.cells.forEach(cell=>{
                                if ( cell.shape === 'image' ) {
                                    cell.ports = {
                                        groups:ports.groups,
                                        items:ports.items.map((item, index)=>({ group:item.group, id:cell.ports.items[index] }))
                                    }
                                }
                            })
                            graph.fromJSON(sourceData);
                            // 应用保存的matrix
                            let  viewportDom = containerRef.current.querySelectorAll('.x6-graph-svg-viewport')[0];
                            viewportDom.setAttribute('transform', sourceData.matrix);                            
                        } else {
                            message.info('还没有保存画布');
                        }
                    }}>测试环境 加载画布</Button>
                    <Button type='primary' icon={<EditOutlined />} onClick={()=>setEditing(true)}>设置</Button>
                </div>
            }       
            {/* 编辑模式下左侧模型库 */}         
            <div style={{ display:isEditing && !hide ? 'block' : 'none', position:'absolute', left:'0', top:'0', width:'15%', height:'100%', background:'#fff', zIndex:'10' }}>
                {/* 制冷站设备模型库 */}
                <Collapse
                    activeKey={['1','2']}
                    className={style['custom-collapse']}
                >
                    <Panel header='制冷站' key='2'>
                        <div className={style['flex-container']}>
                            {
                                frozenStationMeters.map(item=>(
                                    <div 
                                        key={item.key} 
                                        className={style['flex-item']} 
                                        data-title={item.title} 
                                        data-type={item.key} 
                                        data-width={item.width} 
                                        data-height={item.height} 
                                        onMouseDown={startDrag} 
                                    >
                                        <div className={style['flex-item-img']} style={{ backgroundImage:`url(${modelPaths[item.key].path})` }}></div>
                                        <div className={style['flex-item-label']}>{ item.title }</div>
                                    </div>
                                ))
                            }
                        </div>
                    </Panel>
                </Collapse>
            </div>
            
            {/* 编辑模式下右侧属性栏 */}        
            <div style={{ display:Object.keys(currentCell).length ? 'block' : 'none', position:'absolute', right:'0', top:'0', width:'15%', height:'100%', background:'#fff', zIndex:'10' }}>
                <Card
                    title={currentCell.title || '边线' }
                    bodyStyle={{ padding:'0' }}
                >
                    {
                        currentCell.type === 'image' 
                        ?
                        <div className={style['list-container']}>
                            { meterId && <Button type='primary' onClick={()=>unbindMeter() }>解除绑定</Button> }
                            <div className={style['list-item']}>
                                <div className={style['list-item-label']}>绑定电表</div>
                                <div className={style['list-item-content']}>
                                    <Select style={{ width:'100%' }} value={meterId} onChange={value=>setMeterId(value)}>
                                        {
                                            meterList.map((item, index)=>(
                                                <Option key={index} value={item.key} disabled={ item.key === meterId || !selectedIds.includes(item.key) ? false : true }>{ item.meterName }</Option>
                                            ))
                                        }
                                    </Select>
                                </div>
                            </div>
                        </div>
                        :
                        currentCell.type === 'edge' 
                        ?
                        <div className={style['list-container']}>
                            {
                                edgeAttrs.map((item, index)=>(
                                    <AttrListItem 
                                        key={ item.parentKey ? item.parentKey + '-' + item.key : item.key}
                                        attrs={attrs} 
                                        data={item} 
                                        onUpdateAttr={(key, parentKey, value, valueType)=>updateAttr(key, parentKey, value, valueType)} 
                                    />
                                ))
                            }
                        </div>
                        :
                        null
                    }                    
                </Card>
            </div>
             
            {/* X6的渲染画布 */}
            <div ref={containerRef} style={{ height:'100%' }}></div>
        </div>
    )
}

export default React.memo(CanvasContainer);